const BbPromise = require('bluebird');
const fse = require('fs-extra');
const path = require('path');
const JSZip = require('jszip');
const { writeZip, addTree } = require('./zipTree');
const { sha256Path, getRequirementsLayerPath } = require('./shared');

BbPromise.promisifyAll(fse);

/**
 * Zip up requirements to be used as layer package.
 * @return {Promise} the JSZip object constructed.
 */
function zipRequirements() {
  const src = path.join('.serverless', 'requirements');
  const reqChecksum = sha256Path(path.join('.serverless', 'requirements.txt'));
  const targetZipPath = path.join('.serverless', 'pythonRequirements.zip');
  const zipCachePath = getRequirementsLayerPath(
    reqChecksum,
    targetZipPath,
    this.options,
    this.serverless
  );

  const promises = [];
  if (fse.existsSync(zipCachePath)) {
    let layerProgress;
    if (this.progress && this.log) {
      layerProgress = this.progress.get('python-layer-requirements');
      layerProgress.update(
        'Using cached Python Requirements Lambda Layer file'
      );
      this.log.info('Found cached Python Requirements Lambda Layer file');
    } else {
      this.serverless.cli.log(
        'Found cached Python Requirements Lambda Layer file'
      );
    }
  } else {
    const rootZip = new JSZip();
    const runtimepath = 'python';

    promises.push(
      addTree(rootZip.folder(runtimepath), src).then(() =>
        writeZip(rootZip, zipCachePath)
      )
    );
  }
  return BbPromise.all(promises).then(() => {
    if (zipCachePath !== targetZipPath) {
      if (process.platform === 'win32') {
        fse.copySync(zipCachePath, targetZipPath);
      } else {
        fse.symlink(zipCachePath, targetZipPath, 'file');
      }
    }
  });
}

/**
 * Creates a layer on the serverless service for the requirements zip.
 * @return {Promise} empty promise
 */
function createLayers() {
  if (!this.serverless.service.layers) {
    this.serverless.service.layers = {};
  }
  this.serverless.service.layers['pythonRequirements'] = Object.assign(
    {
      artifact: path.join('.serverless', 'pythonRequirements.zip'),
      name: `${
        this.serverless.service.service
      }-${this.serverless.providers.aws.getStage()}-python-requirements`,
      description:
        'Python requirements generated by serverless-python-requirements.',
      compatibleRuntimes: [this.serverless.service.provider.runtime],
    },
    this.options.layer
  );

  return BbPromise.resolve();
}

/**
 * Creates a layer from the installed requirements.
 * @return {Promise} the combined promise for requirements layer.
 */
function layerRequirements() {
  if (!this.options.layer) {
    return BbPromise.resolve();
  }

  let layerProgress;
  if (this.progress && this.log) {
    layerProgress = this.progress.get('python-layer-requirements');
    layerProgress.update('Packaging Python Requirements Lambda Layer');
    this.log.info('Packaging Python Requirements Lambda Layer');
  } else {
    this.serverless.cli.log('Packaging Python Requirements Lambda Layer...');
  }

  return BbPromise.bind(this)
    .then(zipRequirements)
    .then(createLayers)
    .finally(() => layerProgress && layerProgress.remove());
}

module.exports = {
  layerRequirements,
};
